package top.xierongsheng.code.redis;

import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.RedisConnectionFailureException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.*;
import org.springframework.stereotype.Service;

import jakarta.annotation.Resource;
import java.io.Serializable;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * 创建redis服务
 *
 * @auther 谢荣生
 * @datetime 2020/10/19 14:00
 */
@Service
public class RedisService {

    @Resource(name = "redisTemplate")
    private RedisTemplate redisTemplate;

    /**
     * 写入缓存
     *
     * @param key
     * @param value
     * @return
     */
    public boolean set(final String key, Object value) {
        boolean result = false;
        try {
            ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
            operations.set(key, value);
            result = true;
        } catch (RedisConnectionFailureException re) {
            set(key, value);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 写入缓存并设置时效时间
     *
     * @param key
     * @param value
     * @param expireTime 单位 秒
     * @return
     */
    public boolean set(final String key, Object value, Long expireTime) {
        boolean result = false;
        try {
            ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
            operations.set(key, value);
            redisTemplate.expire(key, expireTime, TimeUnit.SECONDS);
            result = true;
        } catch (RedisConnectionFailureException re) {
            set(key, value, expireTime);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 读取缓存
     *
     * @param key
     * @return
     */
    public Object get(final String key) {
        Object result = null;
        try {
            ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
            result = operations.get(key);
        } catch (RedisConnectionFailureException re) {
            get(key);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 根据指定key获取剩余有效时间
     *
     * @param key
     * @return
     */
    public Long getKeyExpire(final String key) {
        try {
            return redisTemplate.getExpire(key, TimeUnit.SECONDS);
        } catch (RedisConnectionFailureException re) {
            getKeyExpire(key, TimeUnit.SECONDS);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return 0L;
    }

    /**
     * 根据指定key获取剩余有效时间
     *
     * @param key
     * @param timeUnit
     * @return
     */
    public Long getKeyExpire(final String key, TimeUnit timeUnit) {
        try {
            return redisTemplate.getExpire(key, timeUnit);
        } catch (RedisConnectionFailureException re) {
            getKeyExpire(key, timeUnit);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return 0L;
    }

    /**
     * 缓存Map
     *
     * @param key
     * @param dataMap
     * @return
     */
    public <T> HashOperations<String, Integer, T> setCacheIntegerMap(String key, Map<Integer, T> dataMap) {
        try {
            HashOperations hashOperations = redisTemplate.opsForHash();
            if (null != dataMap) {
                hashOperations.putAll(key, dataMap);
            }
            return hashOperations;
        } catch (RedisConnectionFailureException re) {
            setCacheIntegerMap(key, dataMap);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public <T> HashOperations<String, Long, T> setCacheLongMap(String key, Map<Long, T> dataMap) {
        try {
            HashOperations hashOperations = redisTemplate.opsForHash();
            if (null != dataMap) {
                hashOperations.putAll(key, dataMap);
            }
            return hashOperations;
        } catch (RedisConnectionFailureException re) {
            setCacheLongMap(key, dataMap);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 获得缓存的Map
     *
     * @param key
     * @return
     */
    public <T> Map<Integer, T> getCacheIntegerMap(String key) {
        try {
            return redisTemplate.opsForHash().entries(key);
        } catch (RedisConnectionFailureException re) {
            getCacheIntegerMap(key);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public <T> Map<Object, T> getCacheMap(String key) {
        return redisTemplate.opsForHash().entries(key);
    }

    /**
     * 获得缓存的Map
     *
     * @param key
     * @return
     */
    public <T> Map<Long, T> getCacheLongMap(String key) {
        try {
            return redisTemplate.opsForHash().entries(key);
        } catch (RedisConnectionFailureException re) {
            getCacheLongMap(key);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    /**
     * 写入缓存设置时效时间
     * /ms
     *
     * @param key
     * @param value
     * @return
     */
    public boolean setExpire(final String key, Object value, Long expireTime) {
        boolean result = false;
        try {
            ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
            operations.set(key, value);
            redisTemplate.expire(key, expireTime, TimeUnit.MILLISECONDS);
            result = true;
        } catch (RedisConnectionFailureException re) {
            set(key, value, expireTime);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }


    /**
     * 批量删除对应的value
     *
     * @param keys
     */
    public void remove(final String... keys) {
        try {
            for (String key : keys) {
                remove(key);
            }
        } catch (RedisConnectionFailureException re) {
            remove(keys);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 批量删除key
     *
     * @param pattern
     */
    public void removePattern(final String pattern) {
        try {
            Set<Serializable> keys = redisTemplate.keys(pattern);
            if (keys.size() > 0) {
                redisTemplate.delete(keys);
            }
        } catch (RedisConnectionFailureException re) {
            removePattern(pattern);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 删除对应的value
     *
     * @param key
     */
    public void remove(final String key) {
        try {
            if (exists(key)) {
                redisTemplate.delete(key);
            }
        } catch (RedisConnectionFailureException re) {
            remove(key);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 判断缓存中是否有对应的value
     *
     * @param key
     * @return
     */
    public boolean exists(final String key) {
        try {
            return redisTemplate.hasKey(key);
        } catch (RedisConnectionFailureException re) {
            exists(key);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }


    /**
     * 哈希 添加
     *
     * @param key
     * @param hashKey
     * @param value
     */
    public void hmSet(String key, Object hashKey, Object value) {
        try {
            HashOperations<String, Object, Object> hash = redisTemplate.opsForHash();
            hash.put(key, hashKey, value);
        } catch (RedisConnectionFailureException re) {
            hmSet(key, hashKey, value);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 哈希获取数据
     *
     * @param key
     * @param hashKey
     * @return
     */
    public Object hmGet(String key, Object hashKey) {
        try {
            HashOperations<String, Object, Object> hash = redisTemplate.opsForHash();
            return hash.get(key, hashKey);
        } catch (RedisConnectionFailureException re) {
            hmGet(key, hashKey);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    /**
     * 批量获取哈希值 根据keys
     *
     * @param key
     * @param hashKey
     * @param <T>
     * @return
     */
    public <T> List<T> HmGetEntry(String key, List hashKey) {
        try {
            HashOperations<String, Object, Object> hash = redisTemplate.opsForHash();
            List<T> list = hash.multiGet(key, hashKey);
            return list;
        } catch (RedisConnectionFailureException re) {
            HmGetEntry(key, hashKey);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 根据key删除
     *
     * @param key
     * @param hashKey
     * @return
     */
    public boolean delhm(String key, Object hashKey) {
        try {
            redisTemplate.opsForHash().delete(key, hashKey);
            return true;
        } catch (RedisConnectionFailureException re) {
            delhm(key, hashKey);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }


    /**
     * 哈希获取所有key下的map记录
     *
     * @param key
     * @return
     */
    public Object hmGetAll(String key) {
        try {
            return redisTemplate.opsForHash().entries(key);
        } catch (RedisConnectionFailureException re) {
            hmGetAll(key);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 设置所有哈希map记录
     *
     * @param key
     * @return
     */
    public <T> boolean hmSetAll(String key, Map<T, Object> dataMap) {
        boolean result = false;
        try {
            if (null != dataMap) {
                redisTemplate.opsForHash().putAll(key, dataMap);
            }
            result = true;
        } catch (RedisConnectionFailureException re) {
            hmSetAll(key, dataMap);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 列表添加
     *
     * @param k
     * @param v
     */
    public void lPush(String k, Object v) {
        try {
            ListOperations<String, Object> list = redisTemplate.opsForList();
            list.rightPush(k, v);
        } catch (RedisConnectionFailureException re) {
            lPush(k, v);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 列表获取
     *
     * @param k
     * @param l
     * @param l1
     * @return
     */
    public List<Object> lRange(String k, long l, long l1) {
        try {
            ListOperations<String, Object> list = redisTemplate.opsForList();
            return list.range(k, l, l1);
        } catch (RedisConnectionFailureException re) {
            lRange(k, l, l1);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 获取列表长度
     *
     * @param key
     * @return
     */
    public long lgetSize(String key) {
        try {
            ListOperations<String, Object> list = redisTemplate.opsForList();
            return list.size(key);
        } catch (RedisConnectionFailureException re) {
            lgetSize(key);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return 0;

    }

    /**
     * 列表头插入
     */
    public long addLeft(String key, Object value) {
        try {
            ListOperations<String, Object> list = redisTemplate.opsForList();
            return list.leftPush(key, value);
        } catch (RedisConnectionFailureException re) {
            addLeft(key, value);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return 0;
    }

    /**
     * 集合添加
     *
     * @param key
     * @param value
     */
    public void add(String key, Object value) {
        try {
            SetOperations<String, Object> set = redisTemplate.opsForSet();
            set.add(key, value);
        } catch (RedisConnectionFailureException re) {
            add(key, value);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 随机获取几个值
     * spring 继承的redis 会出现重复 除非修改底层
     *
     * @param key
     * @return
     */
    public List<Object> sGetValueRandom(String key, int count) {
        try {
            SetOperations<String, Object> set = redisTemplate.opsForSet();
            return set.randomMembers(key, count);
        } catch (RedisConnectionFailureException re) {
            sGetValueRandom(key, count);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    /**
     * 随机获取几个值 不重复
     * spring 继承的redis 会出现重复 除非修改底层
     *
     * @param key
     * @return
     */
    public Set<Object> sGetRandom(String key, int count) {
        try {
            SetOperations<String, Object> set = redisTemplate.opsForSet();
            return set.distinctRandomMembers(key, count);
        } catch (RedisConnectionFailureException re) {
            sGetRandom(key, count);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 随机获取几个值
     *
     * @param key
     * @return
     */
    public Set<Object> sGetValue(String key) {
        try {
            SetOperations<String, Object> set = redisTemplate.opsForSet();
            return set.members(key);
        } catch (RedisConnectionFailureException re) {
            sGetValue(key);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 获取set size
     *
     * @param key
     * @param
     * @return
     */
    public long countSet(String key) {
        try {
            SetOperations<String, Object> set = redisTemplate.opsForSet();
            return set.size(key);
        } catch (RedisConnectionFailureException re) {
            countSet(key);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return 0L;
    }

    /**
     * 集合获取
     *
     * @param key
     * @return
     */
    public Set<Object> setMembers(String key) {
        try {
            SetOperations<String, Object> set = redisTemplate.opsForSet();
            return set.members(key);
        } catch (RedisConnectionFailureException re) {
            setMembers(key);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 有序集合添加
     *
     * @param key
     * @param value
     * @param scoure
     */
    public void zAdd(String key, Object value, double scoure) {
        try {
            ZSetOperations<String, Object> zset = redisTemplate.opsForZSet();
            zset.add(key, value, scoure);
        } catch (RedisConnectionFailureException re) {
            zAdd(key, value, scoure);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 有序集合返回排名降序
     *
     * @param key
     * @param value
     * @param
     */
    public Long zRank(String key, Object value) {
        try {
            ZSetOperations<String, Object> zset = redisTemplate.opsForZSet();
            Long rank = zset.reverseRank(key, value);
            if (rank == null) {
                rank = 0L;
            } else {
                rank += 1;
            }
            return rank;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return 0L;
    }

    /**
     * 有序集合获取成员分数
     *
     * @param key
     * @param value
     */
    public double zgetScore(String key, Object value) {
        try {
            ZSetOperations<String, Object> zset = redisTemplate.opsForZSet();
            return zset.score(key, value);
        } catch (RedisConnectionFailureException re) {
            zgetScore(key, value);
        } catch (Exception e) {
        }
        return 0;
    }

    /**
     * 有序集合获取
     *
     * @param key
     * @param scoure
     * @param scoure1
     * @return
     */
    public Set<Object> rangeByScore(String key, double scoure, double scoure1) {
        try {
            ZSetOperations<String, Object> zset = redisTemplate.opsForZSet();
            return zset.rangeByScore(key, scoure, scoure1);
        } catch (RedisConnectionFailureException re) {
            rangeByScore(key, scoure, scoure1);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 有序集合获取
     *
     * @param key
     * @param value
     * @return
     */
    public boolean isExist(String key, Object value) {
        try {
            ZSetOperations<String, Object> zset = redisTemplate.opsForZSet();
            return zset.equals(value);
        } catch (RedisConnectionFailureException re) {
            isExist(key, value);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 有序集合获取成员总数
     *
     * @param key
     */
    public long zgetSize(String key) {
        try {
            ZSetOperations<String, Object> zset = redisTemplate.opsForZSet();
            return zset.size(key);
        } catch (RedisConnectionFailureException re) {
            zgetSize(key);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return 0;
    }

    /**
     * 有序集合获取(取前n名)
     *
     * @param key
     * @param start
     * @param end
     * @return
     */
    public Set<Object> rangeByScoreLimit(String key, long start, long end) {
        try {
            ZSetOperations<String, Object> zset = redisTemplate.opsForZSet();
            return zset.reverseRange(key, start, end);
        } catch (RedisConnectionFailureException re) {
            zgetSize(key);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    /**
     * 有序集合获取(取前n名)
     *
     * @param key
     * @param start
     * @param end
     * @return
     */
    public Set<ZSetOperations.TypedTuple<Object>> rangeDesc(String key, long start, long end) {
        try {
            ZSetOperations<String, Object> zset = redisTemplate.opsForZSet();
            Set<ZSetOperations.TypedTuple<Object>> obj = zset.reverseRangeWithScores(key, start, end);
            return obj;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    /**
     * 有序根据分数集合获取(取前n名)
     *
     * @param key
     * @param start
     * @param end
     * @return
     */
    public Set<ZSetOperations.TypedTuple<Object>> rangeWithScoreDesc(String key, double min, double max, long start, long end) {
        try {
            ZSetOperations<String, Object> zset = redisTemplate.opsForZSet();
            Set<ZSetOperations.TypedTuple<Object>> obj = zset.reverseRangeByScoreWithScores(key, min, max, start, end);
            return obj;
        } catch (RedisConnectionFailureException re) {
            rangeDesc(key, start, end);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    /**
     * 获取范围内的数据
     *
     * @param key
     * @param start
     * @param end
     * @return
     */
    public Set<Object> Zrange(String key, long start, long end) {
        try {
            ZSetOperations<String, Object> zset = redisTemplate.opsForZSet();
            return zset.range(key, start, end);
        } catch (RedisConnectionFailureException re) {
            Zrange(key, start, end);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    /**
     * 获取缓存长度
     *
     * @param key
     * @return
     */
    public long zsetSize(String key) {
        try {
            ZSetOperations<String, Object> zset = redisTemplate.opsForZSet();
            long count = zset.zCard(key);
            return count;
        } catch (RedisConnectionFailureException re) {
            zsetSize(key);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return 0;
    }


    /**
     * 判断某个key是否存在
     *
     * @param key
     * @return
     */
    public boolean hasKey(String key) {
        try {
            return redisTemplate.hasKey(key);
        } catch (RedisConnectionFailureException re) {
            hasKey(key);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 有序集合score变化
     *
     * @param key
     * @param value
     * @param date
     */
    public double zScoreChange(String key, Object value, double date) {
        try {
            ZSetOperations<String, Object> zset = redisTemplate.opsForZSet();
            return zset.incrementScore(key, value, date);
        } catch (RedisConnectionFailureException re) {
            zScoreChange(key, value, date);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return 0;
    }


    /**
     * 移除排名信息
     *
     * @param key
     * @param value
     * @return
     */
    public double zScoreRemove(String key, Object value) {
        try {
            ZSetOperations<String, Object> zset = redisTemplate.opsForZSet();
            return zset.remove(key, value);
        } catch (RedisConnectionFailureException re) {
            zScoreRemove(key, value);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return 0;
    }

    public long currtTimeForRedis() {
        return (long) redisTemplate.execute(new RedisCallback<Object>() {
            @Override
            public Object doInRedis(RedisConnection redisConnection) throws DataAccessException {
                return redisConnection.time();
            }
        });
    }

    /**
     * 分布式集群，获取时间方法
     *
     * @return
     */
    public Date getNowTime() {
        try {
            //设置默认时区
            TimeZone tz = TimeZone.getTimeZone("Asia/Shanghai");
            TimeZone.setDefault(tz);
            long timestamp = currtTimeForRedis();
            if (timestamp > 0) {
                return new Date(timestamp);
            } else {
                return new Date();
            }
        } catch (RedisConnectionFailureException re) {
            getNowTime();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return new Date();
    }


    /**
     * 哈希是否存在key
     *
     * @param key
     * @param hashKey
     * @return
     */
    public boolean hasHashKey(String key, Object hashKey) {
        try {
            return redisTemplate.opsForHash().hasKey(key, hashKey);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 按条件返回Hash所有keys
     *
     * @param pattern
     * @return
     */
    public Set<Object> getHashAllKeysByPattern(String pattern) {
        Set<Object> keys = redisTemplate.opsForHash().keys(pattern);
        return keys;
    }

    /**
     * 按条件返回所有keys
     *
     * @param pattern
     * @return
     */
    public Set<Object> getAllKeysByPattern(String pattern) {
        try {
            Set<Object> keys = redisTemplate.keys(pattern + "*");
            return keys;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 确认集合中是否有某个成员存在
     *
     * @param key
     * @return
     */
    public boolean setisMember(String key, Object obj) {
        SetOperations<String, Object> set = redisTemplate.opsForSet();
        return set.isMember(key, obj);
    }

    /**
     * 设置时效超时时间
     *
     * @param key
     * @param expireTime
     * @return
     */
    public boolean setTimeOut(final String key, Long expireTime) {
        boolean result = false;
        try {
            redisTemplate.expire(key, expireTime, TimeUnit.SECONDS);
            result = true;
        } catch (RedisConnectionFailureException re) {
            setTimeOut(key, expireTime);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    public Set<Object> getKeysBylikePattern(String pattern) {
        Set<Object> keys = redisTemplate.keys(pattern + "*");
        return keys;
    }
}
